package com.cxx.purchasecharge.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import com.cxx.purchasecharge.core.config.PurchaseChargeProperties;
import com.cxx.purchasecharge.core.model.GoodsStorageCourse;
import com.cxx.purchasecharge.core.model.OrderStatusCode;
import com.cxx.purchasecharge.core.model.OrderTypeCode;
import com.cxx.purchasecharge.core.model.persistence.Goods;
import com.cxx.purchasecharge.core.model.persistence.GoodsStorage;
import com.cxx.purchasecharge.core.model.persistence.Order;
import com.cxx.purchasecharge.core.model.persistence.OrderItem;
import com.cxx.purchasecharge.core.model.persistence.Payment;
import com.cxx.purchasecharge.dal.CustomerDao;
import com.cxx.purchasecharge.dal.GoodsDao;
import com.cxx.purchasecharge.dal.GoodsStorageDao;
import com.cxx.purchasecharge.dal.OrderDao;
import com.cxx.purchasecharge.dal.PaymentDao;
import com.cxx.purchasecharge.service.CoreService;
import com.cxx.purchasecharge.service.exception.PcServiceException;

@Repository ("coreService")
@Transactional
public class CoreServiceImpl implements CoreService
{
    private static final Logger logger = Logger.getLogger (CoreServiceImpl.class);

    @Autowired
    private OrderDao baseOrderDao;
    @Autowired
    private CustomerDao baseCustomerDao;
    @Autowired
    private PaymentDao paymentDao;
    @Autowired
    private GoodsDao goodsDao;
    @Autowired
    private GoodsStorageDao storageDao;

    @Override
    public Order saveOrder (Order order)
    {

        try
        {
            validateOrder (order);
            order = preprocessOrder (order);
            order = baseOrderDao.save (order);
            updateStorage (order);
            updateGoodsImportPrice (order);
            if (OrderTypeCode.IN == order.getTypeCode ())
            {
                addPayment (order, PurchaseChargeProperties.getAddInorderPaidComment ());
                baseCustomerDao.addDealMoney (order.getReceivable (), order.getCustomer ().getId ());
            }
            if (OrderTypeCode.IN_RETURN == order.getTypeCode () || OrderTypeCode.OUT_RETURN == order.getTypeCode ())
            {
                addPayment (order,
                            OrderTypeCode.IN_RETURN == order.getTypeCode () ? PurchaseChargeProperties.getDelInorderPaidComment ()
                                                                           : PurchaseChargeProperties.getDelOutorderPaidComment ());
                baseCustomerDao.addDealMoney (-(order.getReceivable ()), order.getCustomer ().getId ());
            }
            if (order.getPaidMoney () > 0)
            {
                // 改为以财务收到款为准
                // addPayment (order, "");
            }
            return order;
        }
        catch (Exception e)
        {
            logger.warn ("Save order fail.", e);
        }
        return null;
    }

    @Override
    @Transactional
    public boolean updateOrderStatus (long id, OrderStatusCode statusCode, String comment)
    {
        try
        {
            baseOrderDao.updateStatus (statusCode, comment, id);
            Order order = baseOrderDao.findOne (id);
            if (OrderTypeCode.OUT == order.getTypeCode ())
            {
                if (OrderStatusCode.FINISHED.equals (statusCode))
                {
                    addPayment (order,
                                OrderTypeCode.IN == order.getTypeCode () ? PurchaseChargeProperties.getAddInorderPaidComment ()
                                                                        : PurchaseChargeProperties.getAddOutorderPaidComment ());
                    baseCustomerDao.addDealMoney (order.getReceivable (), order.getCustomer ().getId ());
                }
                else if (OrderStatusCode.CANCEL.equals (statusCode))
                {
                    if (null != order)
                    {
                        List <OrderItem> orderItems = order.getOrderItems ();
                        List <GoodsStorage> storages = new ArrayList <GoodsStorage> ();
                        if (!CollectionUtils.isEmpty (orderItems))
                        {
                            for (OrderItem orderItem : orderItems)
                            {
                                int amount = orderItem.getAmount ();
                                GoodsStorage storage = orderItem.getGoods ().getStorage ();
                                storage.setCurrentStock (storage.getCurrentStock () + amount);
                                storages.add (storage);
                            }
                        }
                        storageDao.save (storages);

                        // clear profit
                        baseOrderDao.updateReceivable (0, order.getId ());
                        baseOrderDao.updateProfit (0, order.getId ());
                    }
                }
            }

            return true;
        }
        catch (Exception e)
        {
            logger.warn ("Update Order status fail.", e);
            return false;
        }
    }

    @Override
    public void deleteOrder (Order order) throws PcServiceException
    {
        try
        {
            baseOrderDao.delete (order);
        }
        catch (Exception e)
        {
            logger.warn ("Delete Order list fail.", e);
        }
    }

    @Override
    public void deleteOrders (List <Order> orders) throws PcServiceException
    {
        try
        {
            baseOrderDao.delete (orders);
        }
        catch (Exception e)
        {
            logger.warn ("Delete Order list fail.", e);
        }
    }

    private void validateOrder (Order order)
    {
        if (null == order)
        {
            throw new PcServiceException ("Order is null.");
        }
        if (null == order.getCustomer ())
        {
            throw new PcServiceException ("Customer of Order is null.");
        }
    }

    /**
     * 在新增或更新前做一些预处理，如填充必填字段，计算总额和利润
     * 
     * @param order
     * @return
     */
    private Order preprocessOrder (Order order)
    {
        long id = order.getId ();
        Order dbOrder = baseOrderDao.findOne (id);
        if (null != dbOrder)
        {
            // update
            dbOrder.setLastUpdated (new Date ());
            if (order.getPaidMoney () != dbOrder.getPaidMoney ())
            {
                dbOrder.setPayDate (new Date ());
            }
            calculateDealPayAndProfit (order);
            dbOrder.setDealMoney (order.getDealMoney ());
            return dbOrder;
        }
        else
        {
            // new
            if (OrderTypeCode.IN == order.getTypeCode () || OrderTypeCode.IN_RETURN == order.getTypeCode ()
                || OrderTypeCode.OUT_RETURN == order.getTypeCode ())
            {
                order.setStatusCode (OrderStatusCode.FINISHED);
            }
            order.setCreateDate (new Date ());
            order.setLastUpdated (new Date ());
            order.setPayDate (new Date ());
            calculateDealPayAndProfit (order);
            return order;
        }
    }

    /**
     * 计算总额和利润
     * 
     * @param order
     */
    private void calculateDealPayAndProfit (Order order)
    {
        float sum = 0;
        float profit = 0;
        if (null != order)
        {
            List <OrderItem> orderItems = order.getOrderItems ();
            if (!CollectionUtils.isEmpty (orderItems))
            {
                for (OrderItem orderItem : orderItems)
                {
                    sum += orderItem.getUnitPrice () * orderItem.getAmount ();
                    profit += (orderItem.getUnitPrice () - orderItem.getGoods ().getImportPrice ())
                              * orderItem.getAmount ();
                }
                order.setDealMoney (sum);
                order.setProfit (profit);
            }
        }
    }

    private void updateStorage (Order order)
    {
        if (OrderTypeCode.IN == order.getTypeCode () || OrderTypeCode.OUT_RETURN == order.getTypeCode ())
        {
            List <OrderItem> orderItems = order.getOrderItems ();
            if (!CollectionUtils.isEmpty (orderItems))
            {
                for (OrderItem orderItem : orderItems)
                {
                    GoodsStorage storage = storageDao.findByGoods (orderItem.getGoods ().getId ());
                    if (null != storage)
                    {
                        storage.setLastUpdated (new Date ());
                        storage.setCurrentStock (storage.getCurrentStock () + orderItem.getAmount ());
                        storage.setUserUpdatedBy (order.getUserSigned ());
                        storageDao.save (storage);
                        updateGoodsImportPrice (orderItem, order.getTypeCode ());
                    }
                }
            }
        }
        else if (OrderTypeCode.IN_RETURN == order.getTypeCode () || OrderTypeCode.OUT == order.getTypeCode ())
        {
            List <OrderItem> orderItems = order.getOrderItems ();
            if (!CollectionUtils.isEmpty (orderItems))
            {
                for (OrderItem orderItem : orderItems)
                {
                    GoodsStorage storage = storageDao.findByGoods (orderItem.getGoods ().getId ());
                    if (null != storage)
                    {
                        storage.setLastUpdated (new Date ());
                        storage.setCurrentStock (storage.getCurrentStock () - orderItem.getAmount ());
                        storage.setUserUpdatedBy (order.getUserSigned ());
                        storageDao.save (storage);
                    }
                }
            }
        }
    }

    private void updateGoodsImportPrice (Order order)
    {
        if (OrderTypeCode.IN == order.getTypeCode ())
        {
            List <OrderItem> orderItems = order.getOrderItems ();
            if (!CollectionUtils.isEmpty (orderItems))
            {
                for (OrderItem orderItem : orderItems)
                {
                    Goods goods = orderItem.getGoods ();
                    Goods dbGoods = goodsDao.findOne (goods.getId ());
                    if (dbGoods.getImportPrice () != orderItem.getUnitPrice ())
                    {
                        dbGoods.setImportPrice (orderItem.getUnitPrice ());
                        goodsDao.save (dbGoods);
                    }
                }
            }
        }
    }

    private void addPayment (Order order, String comment)
    {
        if (null != order)
        {
            Payment payment = new Payment ();
            payment.setPaid (OrderTypeCode.IN == order.getTypeCode () || OrderTypeCode.OUT == order.getTypeCode () ? -order.getDealMoney ()
                                                                                                                  : order.getDealMoney ());
            payment.setAllUnPaid (OrderTypeCode.IN == order.getTypeCode () || OrderTypeCode.OUT == order.getTypeCode () ? baseCustomerDao.findOne (order.getCustomer ()
                                                                                                                                                        .getId ())
                                                                                                                                         .getDealMoney ()
                                                                                                                          + order.getReceivable ()
                                                                                                                       : baseCustomerDao.findOne (order.getCustomer ()
                                                                                                                                                       .getId ())
                                                                                                                                        .getDealMoney ()
                                                                                                                         - order.getReceivable ());
            payment.setPaidDate (new Date ());
            payment.setCustomer (order.getCustomer ());
            payment.setOrderId (order.getId ());
            payment.setUserCreatedBy (order.getUserSigned ());
            payment.setComment (comment);
            paymentDao.save (payment);
        }
    }

    private void updateGoodsImportPrice (OrderItem orderItem, OrderTypeCode typeCode)
    {
        if (null != orderItem && null != orderItem.getGoods () && null != typeCode && OrderTypeCode.IN == typeCode)
        {
            Goods goods = goodsDao.findById (orderItem.getGoods ().getId ());
            if (goods.getImportPrice () != orderItem.getUnitPrice ())
            {
                goods.setImportPrice (orderItem.getGoods ().getImportPrice ());
                goodsDao.save (goods);
            }
        }
    }

    @Override
    public boolean isLessThanMinStorage (long goodsId) throws PcServiceException
    {
        Goods goods = goodsDao.findOne (goodsId);
        if (null != goods)
        {
            GoodsStorage goodsStorage = goods.getStorage ();
            if (null != goodsStorage)
            {
                if (goodsStorage.getCurrentStock () <= goodsStorage.getMinStock ())
                {
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public Boolean isStorageException (long goodsId) throws PcServiceException
    {
        List <GoodsStorageCourse> storageCourses = goodsDao.findGoodsStorageCourse (goodsId);
        if (!CollectionUtils.isEmpty (storageCourses))
        {
            long inStock = 0;
            long outStock = 0;
            long currentStock = 0;
            long initialStock = 0;
            for (GoodsStorageCourse storageCourse : storageCourses)
            {
                currentStock = storageCourse.getCurrentStock ();
                initialStock = storageCourse.getInitialStock ();
                if (0 == storageCourse.getOrderTypeCode ())
                {
                    inStock += storageCourse.getAmount ();
                }
                if (2 == storageCourse.getOrderTypeCode ())
                {
                    outStock += storageCourse.getAmount ();
                }
            }

            long restStock = initialStock + inStock - outStock;
            if (restStock < currentStock)
            {
                return Boolean.TRUE;
            }
            else if (restStock > currentStock)
            {
                return Boolean.FALSE;
            }
        }
        return null;
    }

    @Override
    public boolean isStorageEnough (long goodsId, int goodsAmount) throws PcServiceException
    {
        Goods goods = goodsDao.findOne (goodsId);
        if (null != goods)
        {
            if (goodsAmount <= goods.getStorage ().getCurrentStock ())
            {
                return true;
            }
        }
        return false;
    }

}
